package sessionshelper

import (
	"code.google.com/p/jesus-1496/auth/models"
	"code.google.com/p/jesus-1496/serverlog"
	"github.com/gorilla/sessions"
	"net/http"
)

type SessionsHelperError struct {
	errorString string
}

func NewSessionsHelperError(es string) error {
	return &SessionsHelperError{es}
}

func (s *SessionsHelperError) Error() string {
	return s.errorString
}

var logger = serverlog.NewServerLog("auth/sessionshelper")

//Will have to make sure I can write to the directory etc.
//var filesystemStoreLocation = path.Join(env.GetProjectDir(), "auth", "sessions", "usersessions")

//Maybe get the secret keys from
var serverStore = sessions.NewFilesystemStore("", []byte("1234567890123456"))
var cookieStore = sessions.NewCookieStore([]byte("1234567890123456"))

func getServerSession(r *http.Request) (*sessions.Session, error) {
	session, err := serverStore.Get(r, "auth-server-session")
	if err == nil && session.IsNew {
		logger.Printf("New server session created")
	}
	return session, err
}

func getCookieSession(r *http.Request) (*sessions.Session, error) {
	session, err := cookieStore.Get(r, "auth-cookie-session")
	if err == nil && session.IsNew {
		logger.Printf("New cookie session created")
	}
	return session, err
}

func LogUserIn(w http.ResponseWriter, r *http.Request, u *models.User) error {
	if u == nil {
		return NewSessionsHelperError("LogUserIn: models.User parameter as nil.")
	}

	sSession, err := getServerSession(r)
	if err != nil {
		logger.Printf("LogUserIn: error getting previous server session. Ignoring error: %v", err)
	}

	cSession, err := getCookieSession(r)
	if err != nil {
		logger.Printf("LogUserIn: error getting previous cookie session. Ignoring error: %v", err)
	}

	sSession.Values["user"] = *u
	sSession.Values["isLoggedIn"] = true
	cSession.Values["email"] = u.Email
	cSession.Values["isLoggedIn"] = true

	sSession.Save(r, w)
	cSession.Save(r, w)
	logger.Printf("LogUserIn: User %s successfully logged in.\n", u.Email)
	return nil
}

//returns user only if user is logged in, returns nil otherwise
func GetSessionUser(w http.ResponseWriter, r *http.Request) (*models.User, error) {
	//See if user is present in the cookie session first.
	cSession, err := getCookieSession(r)
	if err != nil {
		return nil, err
	}

	cIsLoggedInI := cSession.Values["isLoggedIn"]
	if cIsLoggedInI != nil {
		if cIsLoggedInI.(bool) == true {
			//User is logged in, retrieve email
			emailI := cSession.Values["email"]
			if emailI != nil {
				email := emailI.(string)
				if email == "" {
					//Something gone wrong here, isLoggedIn is true, but cannot retrive email
					logger.Printf("GetSessionUser: server session has isLoggedIn as true, but no email present.")

					LogUserOut(w, r) //Ignoring error
					return nil, NewSessionsHelperError("Cannot retrieve email from cookie session despite isLoggedIn flag being true. Logged user out")
				}
				//We have user.
				logger.Printf("Successfully retrieved email from Cookie Session.")
				//Need to try and get user
				user, err := models.LoadUser(email)
				if err != nil {
					logger.Printf("Email retrieved successfully from cookie session but cannot retrieve from User store.")
					LogUserOut(w, r)
					return nil, err
				}
				//Make sure user is logged in
				LogUserIn(w, r, user) //Ignore error
				return user, nil      //SUCCESS!
			}
			logger.Printf("GetSessionUser: server session has isLoggedIn as true, but no email present.")
			LogUserOut(w, r) //Ignoring error
			return nil, NewSessionsHelperError("Cannot retrieve email from cookie session despite isLoggedIn flag being true. Logged user out")
		}
		//User not logged in.
		logger.Printf("User not logged in, calling LogUserOut to make sure that they are fully logged out before returning.")
		LogUserOut(w, r)
		return nil, NewSessionsHelperError("User not logged in, so logged user out and returning.")
	}
	logger.Printf("User not found in cookie session, trying server session.")

	sSession, err := getServerSession(r)
	if err != nil {
		return nil, err
	}

	sIsLoggedInI := sSession.Values["isLoggedIn"]
	if sIsLoggedInI != nil {
		if sIsLoggedInI.(bool) == true {
			//User is logged in, retrieve user
			userI := sSession.Values["user"]
			if userI != nil {
				user, ok := userI.(models.User)
				if ok {
					if user.Email != "" {
						//Great success
						logger.Printf("Successfully retrieved user from Server Session.")
						//Make sure user is fully logged in.
						LogUserIn(w, r, &user)
						return &user, nil

					}
				}
				//Something gone wrong here, isLoggedIn is true, but cannot retrive user
				logger.Printf("GetSessionUser: server session has isLoggedIn as true, but no user present.")
				//Ignoring error
				LogUserOut(w, r)
				return nil, NewSessionsHelperError("Cannot retrieve user from server session despite isLoggedIn flag being true. Logged user out")
			}
			logger.Printf("GetSessionUser: server session has isLoggedIn as true, but no user present.")
			//Ignoring error
			LogUserOut(w, r)
			return nil, NewSessionsHelperError("Cannot retrieve user from server session despite isLoggedIn flag being true. Logged user out")
		}
		//User not logged in.
		logger.Printf("User not logged in, calling LogUserOut to make sure that they are fully logged out before returning.")
		LogUserOut(w, r)
		return nil, NewSessionsHelperError("User not logged in, so logged user out and returning.")
	}

	logger.Printf("User not found in any session store, so logging user out just to make sure before returning.")
	LogUserOut(w, r)
	return nil, NewSessionsHelperError("No user found in either session store. Are you sure user is logged in?")

}

func LogUserOut(w http.ResponseWriter, r *http.Request) error {
	sSession, err := getServerSession(r)
	if err != nil {
		return err
	}

	cSession, err := getCookieSession(r)
	if err != nil {
		return err
	}

	sSession.Values["user"] = nil
	sSession.Values["isLoggedIn"] = false
	cSession.Values["isLoggedIn"] = false

	sSession.Save(r, w)
	cSession.Save(r, w)

	logger.Printf("LogUserOut: User successfully logged out.\n")
	return nil
}
